1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.google.common.testing;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20
21 import com.google.common.annotations.GwtCompatible;
22 import com.google.common.base.Equivalence;
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.Lists;
25
26 import junit.framework.AssertionFailedError;
27
28 import java.util.List;
29
30
31
32
33
34
35
36 @GwtCompatible
37 final class RelationshipTester<T> {
38
39 static class ItemReporter {
40 String reportItem(Item<?> item) {
41 return item.toString();
42 }
43 }
44
45
46
47
48
49
50
51
52 private final Equivalence<? super T> equivalence;
53 private final String relationshipName;
54 private final String hashName;
55 private final ItemReporter itemReporter;
56 private final List<ImmutableList<T>> groups = Lists.newArrayList();
57
58 RelationshipTester(Equivalence<? super T> equivalence, String relationshipName, String hashName,
59 ItemReporter itemReporter) {
60 this.equivalence = checkNotNull(equivalence);
61 this.relationshipName = checkNotNull(relationshipName);
62 this.hashName = checkNotNull(hashName);
63 this.itemReporter = checkNotNull(itemReporter);
64 }
65
66
67 public RelationshipTester<T> addRelatedGroup(Iterable<? extends T> group) {
68 groups.add(ImmutableList.copyOf(group));
69 return this;
70 }
71
72 public void test() {
73 for (int groupNumber = 0; groupNumber < groups.size(); groupNumber++) {
74 ImmutableList<T> group = groups.get(groupNumber);
75 for (int itemNumber = 0; itemNumber < group.size(); itemNumber++) {
76
77 for (int relatedItemNumber = 0; relatedItemNumber < group.size(); relatedItemNumber++) {
78 if (itemNumber != relatedItemNumber) {
79 assertRelated(groupNumber, itemNumber, relatedItemNumber);
80 }
81 }
82
83 for (int unrelatedGroupNumber = 0; unrelatedGroupNumber < groups.size();
84 unrelatedGroupNumber++) {
85 if (groupNumber != unrelatedGroupNumber) {
86 ImmutableList<T> unrelatedGroup = groups.get(unrelatedGroupNumber);
87 for (int unrelatedItemNumber = 0; unrelatedItemNumber < unrelatedGroup.size();
88 unrelatedItemNumber++) {
89 assertUnrelated(groupNumber, itemNumber, unrelatedGroupNumber, unrelatedItemNumber);
90 }
91 }
92 }
93 }
94 }
95 }
96
97 private void assertRelated(int groupNumber, int itemNumber, int relatedItemNumber) {
98 Item<T> itemInfo = getItem(groupNumber, itemNumber);
99 Item<T> relatedInfo = getItem(groupNumber, relatedItemNumber);
100
101 T item = itemInfo.value;
102 T related = relatedInfo.value;
103 assertWithTemplate("$ITEM must be $RELATIONSHIP to $OTHER", itemInfo, relatedInfo,
104 equivalence.equivalent(item, related));
105
106 int itemHash = equivalence.hash(item);
107 int relatedHash = equivalence.hash(related);
108 assertWithTemplate("the $HASH (" + itemHash + ") of $ITEM must be equal to the $HASH ("
109 + relatedHash + ") of $OTHER", itemInfo, relatedInfo, itemHash == relatedHash);
110 }
111
112 private void assertUnrelated(int groupNumber, int itemNumber, int unrelatedGroupNumber,
113 int unrelatedItemNumber) {
114 Item<T> itemInfo = getItem(groupNumber, itemNumber);
115 Item<T> unrelatedInfo = getItem(unrelatedGroupNumber, unrelatedItemNumber);
116
117 assertWithTemplate("$ITEM must not be $RELATIONSHIP to $OTHER", itemInfo, unrelatedInfo,
118 !equivalence.equivalent(itemInfo.value, unrelatedInfo.value));
119 }
120
121 private void assertWithTemplate(String template, Item<T> item, Item<T> other, boolean condition) {
122 if (!condition) {
123 throw new AssertionFailedError(template
124 .replace("$RELATIONSHIP", relationshipName)
125 .replace("$HASH", hashName)
126 .replace("$ITEM", itemReporter.reportItem(item))
127 .replace("$OTHER", itemReporter.reportItem(other)));
128 }
129 }
130
131 private Item<T> getItem(int groupNumber, int itemNumber) {
132 return new Item<T>(groups.get(groupNumber).get(itemNumber), groupNumber, itemNumber);
133 }
134
135 static final class Item<T> {
136 final T value;
137 final int groupNumber;
138 final int itemNumber;
139
140 Item(T value, int groupNumber, int itemNumber) {
141 this.value = value;
142 this.groupNumber = groupNumber;
143 this.itemNumber = itemNumber;
144 }
145
146 @Override public String toString() {
147 return new StringBuilder()
148 .append(value)
149 .append(" [group ")
150 .append(groupNumber + 1)
151 .append(", item ")
152 .append(itemNumber + 1)
153 .append(']')
154 .toString();
155 }
156 }
157 }